{
 "cells": [
  {
   "cell_type": "markdown",
   "id": "d076d708",
   "metadata": {},
   "source": [
    "### 依赖"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 2,
   "id": "4e98640e",
   "metadata": {},
   "outputs": [],
   "source": [
    ":dep chromiumoxide = { version = \"0.5.7\", features = [\"tokio-runtime\"], default-features = false}\n",
    ":dep tokio = { version = \"1.37.0\", features = [\"full\"] }\n",
    ":dep futures = \"0.3.30\"\n",
    ":dep anyhow = \"1.0\""
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 4,
   "id": "a45bb6d1",
   "metadata": {},
   "outputs": [],
   "source": [
    "use std::io;\n",
    "\n",
    "use chromiumoxide::{\n",
    "    browser::{Browser, BrowserConfig},\n",
    "    cdp::\n",
    "        browser_protocol::{\n",
    "            input::{DispatchMouseEventParams, DispatchMouseEventType, MouseButton},\n",
    "            page::NavigateParams,\n",
    "        }\n",
    "    ,\n",
    "    Page,\n",
    "};\n",
    "\n",
    "use futures::StreamExt;\n",
    "use tokio::{\n",
    "    fs::File, io::AsyncWriteExt, task::JoinHandle, time::{sleep, Duration}\n",
    "};"
   ]
  },
  {
   "cell_type": "markdown",
   "id": "14d98f09",
   "metadata": {},
   "source": [
    "### 函数"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 5,
   "id": "28902225",
   "metadata": {},
   "outputs": [],
   "source": [
    "// 处理验证码逻辑\n",
    "pub async fn handle_validate(page: &Page) -> Result<(), Box<dyn std::error::Error>> {\n",
    "    let mut point = page\n",
    "        .find_element(\"span#nc_1_n1z\")\n",
    "        .await?\n",
    "        .clickable_point()\n",
    "        .await?;\n",
    "\n",
    "    let cmd = DispatchMouseEventParams::builder()\n",
    "        .x(point.x)\n",
    "        .y(point.y)\n",
    "        .button(MouseButton::Left);\n",
    "\n",
    "    page.move_mouse(point)\n",
    "        .await?\n",
    "        .execute(\n",
    "            cmd.clone()\n",
    "                .r#type(DispatchMouseEventType::MousePressed)\n",
    "                .build()\n",
    "                .unwrap(),\n",
    "        )\n",
    "        .await?;\n",
    "\n",
    "    point.x += 100.0;\n",
    "    page.move_mouse(point).await?;\n",
    "\n",
    "    sleep(Duration::from_millis(100)).await;\n",
    "    point.x += 100.0;\n",
    "    page.move_mouse(point).await?;\n",
    "\n",
    "    sleep(Duration::from_millis(100)).await;\n",
    "    point.x += 80.0;\n",
    "    point.y += 100.0;\n",
    "    page.move_mouse(point).await?;\n",
    "\n",
    "    sleep(Duration::from_millis(2000)).await;\n",
    "    page.execute(\n",
    "        cmd.r#type(DispatchMouseEventType::MouseReleased)\n",
    "            .build()\n",
    "            .unwrap(),\n",
    "    )\n",
    "    .await?;\n",
    "\n",
    "    Ok(())\n",
    "}\n",
    "\n",
    "// 启动浏览器\n",
    "pub async fn launch_browser() -> Result<(Browser, JoinHandle<()>), Box<dyn std::error::Error>> {\n",
    "    let (browser, mut handler) = Browser::launch(\n",
    "        BrowserConfig::builder()\n",
    "            .no_sandbox()\n",
    "            .with_head()\n",
    "            //.arg(\"--disable-dev-shm-usage\")\n",
    "            //.arg(\"--disable-blink-features=AutomationControlled\")\n",
    "            .build()?,\n",
    "    )\n",
    "    .await?;\n",
    "\n",
    "    let handle = tokio::task::spawn(async move {\n",
    "        while let Some(h) = handler.next().await {\n",
    "            if h.is_err() {\n",
    "                break;\n",
    "            }\n",
    "        }\n",
    "    });\n",
    "\n",
    "    Ok((browser, handle))\n",
    "}\n",
    "\n",
    "// 关闭浏览器\n",
    "pub async fn close_browser(\n",
    "    mut browser: Browser,\n",
    "    handle: JoinHandle<()>,\n",
    ") -> Result<(), Box<dyn std::error::Error>> {\n",
    "    browser.close().await?;\n",
    "    handle.await?;\n",
    "    Ok(())\n",
    "}\n",
    "\n",
    "pub async fn save_string_to_file(data: &str, input_path: &str) -> io::Result<()> {\n",
    "    let mut file = File::create(input_path).await?;\n",
    "    file.write_all(data.as_bytes()).await?;\n",
    "    Ok(())\n",
    "}"
   ]
  },
  {
   "cell_type": "markdown",
   "id": "1c6ceb79",
   "metadata": {},
   "source": [
    "### 测试"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 6,
   "id": "6ea95f08",
   "metadata": {},
   "outputs": [],
   "source": [
    "let url = \"https://detail.1688.com/offer/619309324578.html\";\n",
    "\n",
    "let (browser, handle) = launch_browser().await?;\n",
    "\n",
    "let page = browser.new_page(\"https://www.baidu.com\").await?;\n",
    "\n",
    "page.evaluate_expression(r#\"Object.defineProperty(navigator,\"webdriver\",{get:()=>undefined})\"#).await?;\n",
    "\n",
    "page.goto(NavigateParams::new(url)).await?;\n",
    "\n",
    "let find_result = page.find_element(\"span#nc_1_n1z\").await;\n",
    "if find_result.is_ok() {\n",
    "    handle_validate(&page).await?;\n",
    "}\n",
    "\n",
    "let json: String = page\n",
    "    .evaluate_expression(\"JSON.stringify(window.__INIT_DATA)\")\n",
    "    .await?\n",
    "    .into_value()?;"
   ]
  },
  {
   "cell_type": "markdown",
   "id": "4a44aa28",
   "metadata": {},
   "source": [
    "### 访问图片"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 15,
   "id": "d24df7be",
   "metadata": {},
   "outputs": [
    {
     "data": {
      "text/plain": [
       "()"
      ]
     },
     "execution_count": 15,
     "metadata": {},
     "output_type": "execute_result"
    }
   ],
   "source": [
    "let mut image_list = Vec::new();\n",
    "for item in page.find_elements(\"div.content-detail > p > img\").await? {\n",
    "    let attr = item.attribute(\"data-lazyload-src\").await?;\n",
    "    if attr.is_some() {\n",
    "        image_list.push(attr.unwrap());\n",
    "    }\n",
    "}\n"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 16,
   "id": "62031bc8",
   "metadata": {},
   "outputs": [
    {
     "data": {
      "text/plain": [
       "[\"https://cbu01.alicdn.com/img/ibank/2020/411/949/20757949114_1338478951.jpg\", \"https://cbu01.alicdn.com/img/ibank/2020/644/739/20757937446_1338478951.jpg\", \"https://cbu01.alicdn.com/img/ibank/2020/554/037/20681730455_1338478951.jpg\", \"https://cbu01.alicdn.com/img/ibank/2020/806/661/20839166608_1338478951.jpg\", \"https://cbu01.alicdn.com/img/ibank/2020/104/571/20839175401_1338478951.jpg\", \"https://cbu01.alicdn.com/img/ibank/2020/491/781/20839187194_1338478951.jpg\", \"https://cbu01.alicdn.com/img/ibank/2020/021/559/20757955120_1338478951.jpg\", \"https://cbu01.alicdn.com/img/ibank/2020/106/439/20757934601_1338478951.jpg\", \"https://cbu01.alicdn.com/img/ibank/2020/958/991/20839199859_1338478951.jpg\", \"https://cbu01.alicdn.com/img/ibank/2020/612/787/20681787216_1338478951.jpg\", \"https://cbu01.alicdn.com/img/ibank/2020/325/712/20839217523_1338478951.jpg\", \"https://cbu01.alicdn.com/img/ibank/2020/841/997/20681799148_1338478951.jpg\", \"https://cbu01.alicdn.com/img/ibank/2020/569/367/20681763965_1338478951.jpg\", \"https://cbu01.alicdn.com/img/ibank/2020/632/097/20681790236_1338478951.jpg\"]"
      ]
     },
     "execution_count": 16,
     "metadata": {},
     "output_type": "execute_result"
    }
   ],
   "source": [
    "image_list"
   ]
  }
 ],
 "metadata": {
  "kernelspec": {
   "display_name": "Rust",
   "language": "rust",
   "name": "rust"
  },
  "language_info": {
   "codemirror_mode": "rust",
   "file_extension": ".rs",
   "mimetype": "text/rust",
   "name": "Rust",
   "pygment_lexer": "rust",
   "version": ""
  }
 },
 "nbformat": 4,
 "nbformat_minor": 5
}
